<!DOCTYPE html>
<html lang="en">

<head>
    <title>THREE-MOVE</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <!-- <link type="text/css" rel="stylesheet" href="main.css"> -->
    <link rel="icon" href="/src/assets/k.ico">


    <style>
        * {
            margin: 0;
            padding: 0;
        }

        body {
            background-color: #fff;
            color: #444;
        }

        a {
            color: #08f;
        }
    </style>
</head>

<body>
    <div id="info">
    </div>

    <!-- Import maps polyfill -->
    <!-- Remove this when import maps will be widely supported -->
    <script type="text/javascript" src="./astar.js"></script>

    <script type="importmap">
			{
				"imports": {
                    "three": "../../node_modules/three/build/three.module.js"
                }
			}
		</script>


    <script type="module">

        import * as THREE from 'three';
        import { OrbitControls } from '../../node_modules/three/examples/jsm/controls/OrbitControls.js'
        import { GLTFLoader } from '../../node_modules/three/examples/jsm/loaders/GLTFLoader.js';
        import { GUI } from '../../node_modules/three/examples/jsm/libs/lil-gui.module.min.js';

        let SCREEN_WIDTH = window.innerWidth;
        let SCREEN_HEIGHT = window.innerHeight;
        let gui, mixer, actions = {}, activeAction, previousAction
        let container, stats, model, mesh;
        var camera, scene, renderer, clock;
        let cameraControls;
        const api = { state: 'Walking' };
        let pointer = new THREE.Vector2();//创建二维平面
        let raycaster = new THREE.Raycaster();//创建光线投射对象
        let currentAnimConfig, idleAnimConfig, runAnimConfig;
        let palyerPosition = new THREE.Vector3(), distance, targetPosition;
        let scale = new THREE.Vector3(10, 10, 10);
        let modelActions = 0;
        let guidepost, unrealGuidepost;
        let InstancedBox;
        let maps = {};
        let coordinate = [];
        let resultPath = []; //寻路路径
        let isRun = false; //是否是运动状态
        const size = 300;
        const divisions = 30;


        init();
        animate();
        function initCamera() {
            // CAMERA

            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 4000);
            camera.position.set(-20, 100, 230);
            camera.lookAt(0, 0, 0);
        }
        function initScene() {
            // SCENE
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0xffffff);
            scene.fog = new THREE.Fog(0xffffff, 1000, 4000);
            scene.add(camera);
        }

        function initLights() {
            // LIGHTS
            scene.add(new THREE.AmbientLight(0x222222));
            const light = new THREE.DirectionalLight(0xffffff, 2.25);
            light.position.set(200, 450, 500);
            light.castShadow = true;
            light.shadow.mapSize.width = 1024;
            light.shadow.mapSize.height = 512;
            light.shadow.camera.near = 100;
            light.shadow.camera.far = 1200;
            light.shadow.camera.left = - 1000;
            light.shadow.camera.right = 1000;
            light.shadow.camera.top = 350;
            light.shadow.camera.bottom = - 350;
            scene.add(light);
        }

        function initMesh() {
            const negativeSize = -(size / 2);
            const flex = 1;
            const boxScale = size / divisions;
            // 实例化mesh
            InstancedBox = new THREE.InstancedMesh(
                new THREE.BoxGeometry(boxScale, boxScale, boxScale),
                new THREE.MeshBasicMaterial({ color: 0xff0000 * Math.random(), transparent: true }),
                Math.pow(divisions, 2)
            );
            const matrix = new THREE.Matrix4();
            let barrierCount = 1;
            coordinate = [];
            let nodes = [];
            let ggraph = [];
            // width为0时代表有障碍物
            for (let i = 0; i < divisions; i++) {
                coordinate[i] = new Array();
                ggraph[i] = new Array();
                for (let j = 0; j < divisions; j++) {
                    const x = i * boxScale + negativeSize + boxScale / 2;
                    const y = boxScale / 2;
                    const z = j * boxScale + negativeSize + boxScale / 2;
                    const obj = {
                        x: i,
                        y: j,
                        width: 1,
                        closed: false,
                        f: 0,
                        g: 0,
                        h: 0,
                        parent: null,
                        visited: false,
                    }
                    if (Math.floor(Math.random() * 10) < flex) {
                        matrix.setPosition(x, y, z);
                        InstancedBox.setMatrixAt(barrierCount, matrix);
                        InstancedBox.setColorAt(barrierCount, new THREE.Color().setHex(Math.random() * 0xffffff));
                        obj.width = 0;
                        barrierCount++;
                    }
                    nodes.push(obj)
                    coordinate[i][j] = obj;
                    ggraph[i][j] = obj.width;
                }
            }
            maps = new Graph(ggraph);
            scene.add(InstancedBox)
        }
        function initGround() {
            //  GROUND
            const axesHelper = new THREE.AxesHelper(100);
            scene.add(axesHelper);

            const gridHelper = new THREE.GridHelper(size, divisions, 0xff4400, 0x000000);
            scene.add(gridHelper);

            const geometry = new THREE.TorusGeometry(4, 0.3, 16, 100);
            const material = new THREE.MeshBasicMaterial({ color: 0xff4400 });
            guidepost = new THREE.Mesh(geometry, material);
            guidepost.rotation.x = - Math.PI / 2;
            unrealGuidepost = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: 0xFFC0CB }));
            unrealGuidepost.rotation.x = - Math.PI / 2;
            scene.add(guidepost);
            scene.add(unrealGuidepost);

            mesh = new THREE.Mesh(new THREE.PlaneGeometry(size, size), new THREE.MeshPhongMaterial({ color: 0xFFD39B, depthWrite: false }));
            mesh.rotation.x = - Math.PI / 2;
            scene.add(mesh);
        }
        function initPlayer() {
            //model
            const loader = new GLTFLoader();
            loader.load('./glb/RobotExpressive.glb', function (gltf) {
                model = gltf.scene;
                model.scale.x = 3
                model.scale.y = 3
                model.scale.z = 3
                scene.add(model);
                model.position.x = 5;
                model.position.z = 5;
                palyerPosition.copy(model.position);
                palyerPosition.y = 0;
                guidepost.position.copy(model.position)
                createGUI(model, gltf.animations);
            });
        }

        function init() {
            initCamera();
            initScene();
            initLights();
            initMesh();
            initGround();
            initPlayer();
            container = document.createElement('div');
            document.body.appendChild(container);
            clock = new THREE.Clock();
            // RENDERER
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
            container.appendChild(renderer.domElement);
            document.addEventListener('resize', onWindowResize);
            document.addEventListener('pointermove', onPointerMove);
            cameraControls = new OrbitControls(camera, renderer.domElement);
            cameraControls.enableRotate = true;
            // 限制旋转最大角度
            cameraControls.maxPolarAngle = 4 / Math.PI;
            // cameraControls.minPolarAngle = 4 / Math.PI;
            cameraControls.target.set(0, 50, 0);
            cameraControls.update();
            document.addEventListener('pointerdown', onPointerDown);
            document.addEventListener('keypress', onKeyDown);
        }

        function onKeyDown(e) {
            if (e.key === 'x') {
                actions['Punch'].reset().fadeOut(1).play();
            }
            if (e.key === 'c' || e.key === ' ') {
                actions['Jump'].reset().fadeOut(1).play();
            }
            if (e.key === 'z') {
                actions['Sitting'].reset().fadeOut(0.4).play();
            }
        }
        function onPointerDown(e) {
            pointer.set((e.clientX / renderer.domElement.clientWidth) * 2 - 1, -(e.clientY / renderer.domElement.clientHeight) * 2 + 1);
            let intersectsObjArr = getSelsectOBj(raycaster, e);//通过封装的getSelsectOBj函数获取鼠标选中对象集合，e是点击事件对象
            try {
                if (intersectsObjArr.object && !intersectsObjArr?.instanceId) {
                    caculatePath();
                    directToPath();
                    render();
                }
            } catch (e) {

            }

        }
        function onWindowResize() {
            SCREEN_WIDTH = window.innerWidth;
            SCREEN_HEIGHT = window.innerHeight;
            renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
            camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT;
            camera.updateProjectionMatrix();
        }

        function animate() {
            requestAnimationFrame(animate);
            render();
        }

        // 计算路径
        function caculatePath() {
            const startX = (palyerPosition.x - 5 + size / 2) / 10
            const startY = (palyerPosition.z - 5 + size / 2) / 10
            const endX = (targetPosition.x - 5 + size / 2) / 10
            const endY = (targetPosition.z - 5 + size / 2) / 10
            const start = maps.grid[startX][startY];
            const end = maps.grid[endX][endY];
            resultPath = astar.search(maps, start, end);
            modelActions = 0;
            modelActionAnimation();
        }

        function modelActionAnimation() {
            if (modelActions === resultPath.length) {
                currentAnimConfig = idleAnimConfig
                fadeToAction(0.15);
                return
            }
            const next = {
                x: resultPath[modelActions].x * 10 - size / 2 + 5,
                z: resultPath[modelActions].y * 10 - size / 2 + 5
            }
            
            const lookAtPosition = new THREE.Vector3(next.x, model.position.y, next.z);
            if (model.position.x === next.x && model.position.z === next.z) {
                
                console.log(lookAtPosition);
                modelActions++;
                if (currentAnimConfig !== runAnimConfig) {
                    currentAnimConfig = runAnimConfig;
                    // fadeToAction(1);
                    // model.lookAt(lookAtPosition);
                    fadeToAction(0.1);
                }

                requestAnimationFrame(modelActionAnimation);
                return;
            }
            if (model.position.x > next.x) model.position.x -= 1;
            if (model.position.x < next.x) model.position.x += 1;
            if (model.position.z > next.z) model.position.z -= 1;
            if (model.position.z < next.z) model.position.z += 1;
            console.log('lookAtPosition',lookAtPosition);
            model.lookAt(lookAtPosition);
            requestAnimationFrame(modelActionAnimation);
        }

        function onPointerMove(e) {
            pointer.set((e.clientX / renderer.domElement.clientWidth) * 2 - 1, -(e.clientY / renderer.domElement.clientHeight) * 2 + 1);
            let intersectsObjArr = getSelsectOBj(raycaster, e);//通过封装的getSelsectOBj函数获取鼠标选中对象集合，e是点击事件对象
            if (intersectsObjArr && intersectsObjArr?.object === mesh) {
                targetPosition = intersectsObjArr.point.add(intersectsObjArr.face.normal);
                targetPosition.divideScalar(10).floor().multiplyScalar(10).addScalar(5);
                targetPosition.y = 0;
                unrealGuidepost.position.copy(targetPosition)
                render();
            }
        }

        function createGUI(model, animations) {
            mixer = new THREE.AnimationMixer(model);
            actions = {};

            for (let i = 0; i < animations.length; i++) {
                const clip = animations[i];
                const action = mixer.clipAction(clip);
                actions[clip.name] = action;
            }

            idleAnimConfig = actions['Idle'];
            runAnimConfig = actions['Running'];

            currentAnimConfig = idleAnimConfig;
            currentAnimConfig.play();
        }
        function directToPath() {
            // const x = targetPosition.copy();
            const distence = palyerPosition.distanceTo(targetPosition);
            console.log('targetPosition',targetPosition);
            model.lookAt(targetPosition);
            // model.position.copy(targetPosition);
            palyerPosition.copy(targetPosition)
            guidepost.position.copy(targetPosition)
            // model.translateOnAxis(palyerPosition.copy().sub(targetPosition).normalize().negate(), distence);
        }
        function fadeToAction(duration) {
            // 当前动画 -> 递增
            if (currentAnimConfig) {
                currentAnimConfig.reset().fadeIn(duration).play();
            }
            // 其他动画 -> 递减，如站立动作切换到走路
            if (currentAnimConfig !== idleAnimConfig) {
                idleAnimConfig.reset().fadeOut(duration).play();

            }
            // 其他动画 -> 递减，如走路动作切换到站立
            if (currentAnimConfig !== runAnimConfig) {
                runAnimConfig.reset().fadeOut(duration).play();
            }
        }

        function getSelsectOBj(raycaster, e) {
            //将html坐标系转化为webgl坐标系，并确定鼠标点击位置
            raycaster.setFromCamera(pointer, camera);//以camera为z坐标，确定所点击物体的3D空间位置
            let intersects = raycaster.intersectObjects(scene.children, true);//确定所点击位置上的物体数量集合
            return intersects[0];//返回连线经过的第一个模型，既鼠标点击的模型
        }

        function render() {
            const dt = clock.getDelta();
            if (mixer) mixer.update(dt);
            renderer.render(scene, camera);
        }
    </script>

</body>

</html>